{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The autoreload extension is already loaded. To reload it, use:\n",
      "  %reload_ext autoreload\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "import h5py\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "%matplotlib inline\n",
    "plt.rcParams['figure.figsize'] = (5.0, 4.0) \n",
    "plt.rcParams['image.interpolation'] = 'nearest'\n",
    "plt.rcParams['image.cmap'] = 'gray'\n",
    "\n",
    "#ipython很好用，但是如果在ipython里已经import过的模块修改后需要重新reload就需要这样\n",
    "#在执行用户代码前，重新装入软件的扩展和模块。\n",
    "%load_ext autoreload\n",
    "#autoreload 2：装入所有 %aimport 不包含的模块。\n",
    "%autoreload 2\n",
    "\n",
    "np.random.seed(1)      #指定随机种子"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 1、卷积神经网络\n",
    "- 边界填充"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "constant:  \n",
      "[[[0 0 0 0 0 0 0 0 0 0]\n",
      "  [0 0 1 1 2 2 3 4 0 0]\n",
      "  [0 0 1 1 2 2 3 4 0 0]\n",
      "  [0 0 1 1 2 2 3 4 0 0]\n",
      "  [0 0 0 0 0 0 0 0 0 0]]\n",
      "\n",
      " [[0 0 0 0 0 0 0 0 0 0]\n",
      "  [0 0 0 1 2 3 4 5 0 0]\n",
      "  [0 0 0 1 2 3 4 5 0 0]\n",
      "  [0 0 0 1 2 3 4 5 0 0]\n",
      "  [0 0 0 0 0 0 0 0 0 0]]\n",
      "\n",
      " [[0 0 0 0 0 0 0 0 0 0]\n",
      "  [0 0 1 1 2 2 3 4 0 0]\n",
      "  [0 0 1 1 2 2 3 4 0 0]\n",
      "  [0 0 1 1 2 2 3 4 0 0]\n",
      "  [0 0 0 0 0 0 0 0 0 0]]]\n"
     ]
    }
   ],
   "source": [
    "#constant连续一样的值填充，有constant_values=（x, y）时前面用x填充，后面用y填充。缺省参数是为constant_values=（0,0）\n",
    "\n",
    "# a = np.pad(a,((0,0),(1,1),(0,0),(3,3),(0,0)),'constant',constant_values = (..,..))\n",
    "\n",
    "#比如：\n",
    "import numpy as np\n",
    "arr3D = np.array([[[1, 1, 2, 2, 3, 4],\n",
    "             [1, 1, 2, 2, 3, 4], \n",
    "             [1, 1, 2, 2, 3, 4]], \n",
    "             \n",
    "            [[0, 1, 2, 3, 4, 5], \n",
    "             [0, 1, 2, 3, 4, 5], \n",
    "             [0, 1, 2, 3, 4, 5]], \n",
    "             \n",
    "            [[1, 1, 2, 2, 3, 4], \n",
    "             [1, 1, 2, 2, 3, 4], \n",
    "             [1, 1, 2, 2, 3, 4]]])\n",
    "\n",
    "print('constant:  \\n' + str(np.pad(arr3D, ((0, 0), (1, 1), (2, 2)), 'constant')))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "def zero_pad(X, pad):\n",
    "    \"\"\"\n",
    "    把数据集X的图像边界全部使用0来扩充pad个宽度和高度。\n",
    "    \n",
    "    参数：\n",
    "        X - 图像数据集，维度为（样本数，图像高度，图像宽度，图像通道数）\n",
    "        pad - 整数，每个图像在垂直和水平维度上的填充量\n",
    "    返回：\n",
    "        X_paded - 扩充后的图像数据集，维度为（样本数，图像高度 + 2*pad，图像宽度 + 2*pad，图像通道数）\n",
    "    \n",
    "    \"\"\"\n",
    "    \n",
    "    X_paded = np.pad(X,(\n",
    "                        (0,0),       #样本数，不填充\n",
    "                        (pad,pad),   #图像高度,你可以视为上面填充x个，下面填充y个(x,y)\n",
    "                        (pad,pad),   #图像宽度,你可以视为左边填充x个，右边填充y个(x,y)\n",
    "                        (0,0)),      #通道数，不填充\n",
    "                        'constant', constant_values=0)      #连续一样的值填充\n",
    "    \n",
    "    return X_paded"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x.shape = (2, 3, 3, 2)\n",
      "x_paded.shape = (2, 7, 7, 2)\n",
      "x[1, 1] = [[ 0.90085595 -0.68372786]\n",
      " [-0.12289023 -0.93576943]\n",
      " [-0.26788808  0.53035547]]\n",
      "x_paded[1, 1] = [[0. 0.]\n",
      " [0. 0.]\n",
      " [0. 0.]\n",
      " [0. 0.]\n",
      " [0. 0.]\n",
      " [0. 0.]\n",
      " [0. 0.]]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<matplotlib.image.AxesImage at 0x7f91b56394d0>"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 360x288 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "np.random.seed(1)\n",
    "x = np.random.randn(2,3,3,2)\n",
    "x_paded = zero_pad(x,2)\n",
    "#查看信息\n",
    "\n",
    "print (\"x.shape =\", x.shape)\n",
    "print (\"x_paded.shape =\", x_paded.shape)\n",
    "print (\"x[1, 1] =\", x[1, 1])\n",
    "print (\"x_paded[1, 1] =\", x_paded[1, 1])\n",
    "\n",
    "#绘制图\n",
    "fig, axarr = plt.subplots(1,2)  #一行两列\n",
    "axarr[0].set_title('x')\n",
    "axarr[0].imshow(x[0,:,:,0])\n",
    "axarr[1].set_title('x_paded')\n",
    "axarr[1].imshow(x_paded[0,:,:,0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 单步卷积"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "def conv_single_step(a_slice_prev,W,b):\n",
    "    \"\"\"\n",
    "    在前一层的激活输出的一个片段上应用一个由参数W定义的过滤器。\n",
    "    这里切片大小和过滤器大小相同\n",
    "    \n",
    "    参数：\n",
    "        a_slice_prev - 输入数据的一个片段，维度为（过滤器大小，过滤器大小，上一通道数）\n",
    "        W - 权重参数，包含在了一个矩阵中，维度为（过滤器大小，过滤器大小，上一通道数）\n",
    "        b - 偏置参数，包含在了一个矩阵中，维度为（1,1,1）\n",
    "        \n",
    "    返回：\n",
    "        Z - 在输入数据的片X上卷积滑动窗口（w，b）的结果。\n",
    "    \"\"\"\n",
    "    \n",
    "    s = np.multiply(a_slice_prev,W) + b\n",
    "    \n",
    "    Z = np.sum(s)\n",
    "    \n",
    "    return Z"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Z = -23.16021220252078\n"
     ]
    }
   ],
   "source": [
    "# 测试：\n",
    "np.random.seed(1)\n",
    "\n",
    "#这里切片大小和过滤器大小相同\n",
    "a_slice_prev = np.random.randn(4,4,3)\n",
    "W = np.random.randn(4,4,3)\n",
    "b = np.random.randn(1,1,1)\n",
    "\n",
    "Z = conv_single_step(a_slice_prev,W,b)\n",
    "\n",
    "print(\"Z = \" + str(Z))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 前向传播"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "def conv_forward(A_prev, W, b, hparameters):\n",
    "    \"\"\"\n",
    "    实现卷积函数的前向传播\n",
    "    \n",
    "    参数：\n",
    "        A_prev - 上一层的激活输出矩阵，维度为(m, n_H_prev, n_W_prev, n_C_prev)，（样本数量，上一层图像的高度，上一层图像的宽度，上一层过滤器数量）\n",
    "        W - 权重矩阵，维度为(f, f, n_C_prev, n_C)，（过滤器大小，过滤器大小，上一层的过滤器数量，这一层的过滤器数量）\n",
    "        b - 偏置矩阵，维度为(1, 1, 1, n_C)，（1,1,1,这一层的过滤器数量）\n",
    "        hparameters - 包含了\"stride\"与 \"pad\"的超参数字典。\n",
    "    \n",
    "    返回：\n",
    "        Z - 卷积输出，维度为(m, n_H, n_W, n_C)，（样本数，图像的高度，图像的宽度，过滤器数量）\n",
    "        cache - 缓存了一些反向传播函数conv_backward()需要的一些数据\n",
    "    \"\"\"\n",
    "    \n",
    "    #获取来自上一层数据的基本信息\n",
    "    (m , n_H_prev , n_W_prev , n_C_prev) = A_prev.shape\n",
    "    \n",
    "    #获取权重矩阵的基本信息\n",
    "    ( f , f ,n_C_prev , n_C ) = W.shape\n",
    "    \n",
    "    #获取超参数hparameters的值\n",
    "    stride = hparameters[\"stride\"]\n",
    "    pad = hparameters[\"pad\"]\n",
    "    \n",
    "    #计算卷积后的图像的宽度高度，参考上面的公式，使用int()来进行板除\n",
    "    n_H = int(( n_H_prev - f + 2 * pad )/ stride) + 1\n",
    "    n_W = int(( n_W_prev - f + 2 * pad )/ stride) + 1\n",
    "    \n",
    "    #使用0来初始化卷积输出Z\n",
    "    Z = np.zeros((m,n_H,n_W,n_C))\n",
    "    \n",
    "    #通过A_prev创建填充过了的A_prev_pad\n",
    "    A_prev_pad = zero_pad(A_prev,pad)\n",
    "    \n",
    "    for i in range(m):                              #遍历样本\n",
    "        a_prev_pad = A_prev_pad[i]                  #选择第i个样本的扩充后的激活矩阵\n",
    "        for h in range(n_H):                        #在输出的垂直轴上循环\n",
    "            for w in range(n_W):                    #在输出的水平轴上循环\n",
    "                for c in range(n_C):                #循环遍历输出的通道\n",
    "                    #定位当前的切片位置\n",
    "                    vert_start = h * stride         #竖向，开始的位置\n",
    "                    vert_end = vert_start + f       #竖向，结束的位置\n",
    "                    horiz_start = w * stride        #横向，开始的位置\n",
    "                    horiz_end = horiz_start + f     #横向，结束的位置\n",
    "                    #切片位置定位好了我们就把它取出来,需要注意的是我们是“穿透”取出来的，\n",
    "                    #自行脑补一下吸管插入一层层的橡皮泥就明白了\n",
    "                    a_slice_prev = a_prev_pad[vert_start:vert_end,horiz_start:horiz_end,:]\n",
    "                    #执行单步卷积\n",
    "                    Z[i,h,w,c] = conv_single_step(a_slice_prev,W[: ,: ,: ,c],b[0,0,0,c])\n",
    "      \n",
    "    #数据处理完毕，验证数据格式是否正确\n",
    "    assert(Z.shape == (m , n_H , n_W , n_C ))\n",
    "    \n",
    "    #存储一些缓存值，以便于反向传播使用\n",
    "    cache = (A_prev,W,b,hparameters)\n",
    "    \n",
    "    return (Z , cache)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "np.mean(Z) =  0.15585932488906465\n",
      "cache_conv[0][1][2][3] = [-0.20075807  0.18656139  0.41005165]\n"
     ]
    }
   ],
   "source": [
    "np.random.seed(1)\n",
    "\n",
    "A_prev = np.random.randn(10,4,4,3)\n",
    "W = np.random.randn(2,2,3,8)\n",
    "b = np.random.randn(1,1,1,8)\n",
    "\n",
    "hparameters = {\"pad\" : 2, \"stride\": 1}\n",
    "\n",
    "Z , cache_conv = conv_forward(A_prev,W,b,hparameters)\n",
    "\n",
    "print(\"np.mean(Z) = \", np.mean(Z))\n",
    "print(\"cache_conv[0][1][2][3] =\", cache_conv[0][1][2][3])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 池化层\n",
    "- 前向传播"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [],
   "source": [
    "def pool_forward(A_prev,hparameters,mode=\"max\"):\n",
    "    \"\"\"\n",
    "    实现池化层的前向传播\n",
    "    \n",
    "    参数：\n",
    "        A_prev - 输入数据，维度为(m, n_H_prev, n_W_prev, n_C_prev)\n",
    "        hparameters - 包含了 \"f\" 和 \"stride\"的超参数字典\n",
    "        mode - 模式选择【\"max\" | \"average\"】\n",
    "        \n",
    "    返回：\n",
    "        A - 池化层的输出，维度为 (m, n_H, n_W, n_C)\n",
    "        cache - 存储了一些反向传播需要用到的值，包含了输入和超参数的字典。\n",
    "    \"\"\"\n",
    "    \n",
    "    #获取输入数据的基本信息\n",
    "    (m , n_H_prev , n_W_prev , n_C_prev) = A_prev.shape\n",
    "    \n",
    "    #获取超参数的信息\n",
    "    f = hparameters[\"f\"]\n",
    "    stride = hparameters[\"stride\"]\n",
    "    \n",
    "    #计算输出维度\n",
    "    n_H = int((n_H_prev - f) / stride ) + 1\n",
    "    n_W = int((n_W_prev - f) / stride ) + 1\n",
    "    n_C = n_C_prev\n",
    "    \n",
    "    #初始化输出矩阵\n",
    "    A = np.zeros((m , n_H , n_W , n_C))\n",
    "    \n",
    "    for i in range(m):                              #遍历样本\n",
    "        for h in range(n_H):                        #在输出的垂直轴上循环\n",
    "            for w in range(n_W):                    #在输出的水平轴上循环\n",
    "                for c in range(n_C):                #循环遍历输出的通道\n",
    "                    #定位当前的切片位置\n",
    "                    vert_start = h * stride         #竖向，开始的位置\n",
    "                    vert_end = vert_start + f       #竖向，结束的位置\n",
    "                    horiz_start = w * stride        #横向，开始的位置\n",
    "                    horiz_end = horiz_start + f     #横向，结束的位置\n",
    "                    #定位完毕，开始切割\n",
    "                    a_slice_prev = A_prev[i,vert_start:vert_end,horiz_start:horiz_end,c]\n",
    "                    \n",
    "                    #对切片进行池化操作\n",
    "                    if mode == \"max\":\n",
    "                        A[ i , h , w , c ] = np.max(a_slice_prev)\n",
    "                    elif mode == \"average\":\n",
    "                        A[ i , h , w , c ] = np.mean(a_slice_prev)\n",
    "                        \n",
    "    #池化完毕，校验数据格式\n",
    "    assert(A.shape == (m , n_H , n_W , n_C))\n",
    "    \n",
    "    #校验完毕，开始存储用于反向传播的值\n",
    "    cache = (A_prev,hparameters)\n",
    "    \n",
    "    return A,cache"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "mode = max\n",
      "(2, 1, 1, 3)\n",
      "A = [[[[1.74481176 1.6924546  2.10025514]]]\n",
      "\n",
      "\n",
      " [[[1.19891788 1.51981682 2.18557541]]]]\n",
      "----------------------------\n",
      "(2, 1, 1, 3)\n",
      "mode = average\n",
      "A = [[[[-0.09498456  0.11180064 -0.14263511]]]\n",
      "\n",
      "\n",
      " [[[-0.09525108  0.28325018  0.33035185]]]]\n"
     ]
    }
   ],
   "source": [
    "np.random.seed(1)\n",
    "A_prev = np.random.randn(2,4,4,3)\n",
    "hparameters = {\"f\":4 , \"stride\":1}\n",
    "\n",
    "A , cache = pool_forward(A_prev,hparameters,mode=\"max\")\n",
    "A, cache = pool_forward(A_prev, hparameters)\n",
    "print(\"mode = max\")\n",
    "print(A.shape)\n",
    "print(\"A =\", A)\n",
    "print(\"----------------------------\")\n",
    "A, cache = pool_forward(A_prev, hparameters, mode = \"average\")\n",
    "print(A.shape)\n",
    "print(\"mode = average\")\n",
    "print(\"A =\", A)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 卷基神经网络的反向传播\n",
    "- 卷基层的反向传播"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [],
   "source": [
    "def conv_backward(dZ,cache):\n",
    "    \"\"\"\n",
    "    实现卷积层的反向传播\n",
    "    \n",
    "    参数：\n",
    "        dZ - 卷积层的输出Z的 梯度，维度为(m, n_H, n_W, n_C)\n",
    "        cache - 反向传播所需要的参数，conv_forward()的输出之一\n",
    "        \n",
    "    返回：\n",
    "        dA_prev - 卷积层的输入（A_prev）的梯度值，维度为(m, n_H_prev, n_W_prev, n_C_prev)\n",
    "        dW - 卷积层的权值的梯度，维度为(f,f,n_C_prev,n_C)\n",
    "        db - 卷积层的偏置的梯度，维度为（1,1,1,n_C）\n",
    "    \n",
    "    \"\"\"\n",
    "    #获取cache的值\n",
    "    (A_prev, W, b, hparameters) = cache\n",
    "    \n",
    "    #获取A_prev的基本信息\n",
    "    (m, n_H_prev, n_W_prev, n_C_prev) = A_prev.shape\n",
    "    \n",
    "    #获取dZ的基本信息\n",
    "    (m,n_H,n_W,n_C) = dZ.shape\n",
    "    \n",
    "    #获取权值的基本信息\n",
    "    (f, f, n_C_prev, n_C) = W.shape\n",
    "    \n",
    "    #获取hparaeters的值\n",
    "    pad = hparameters[\"pad\"]\n",
    "    stride = hparameters[\"stride\"]\n",
    "    \n",
    "    #初始化各个梯度的结构\n",
    "    dA_prev = np.zeros((m,n_H_prev,n_W_prev,n_C_prev))\n",
    "    dW = np.zeros((f,f,n_C_prev,n_C))\n",
    "    db = np.zeros((1,1,1,n_C))\n",
    "    \n",
    "    #前向传播中我们使用了pad，反向传播也需要使用，这是为了保证数据结构一致\n",
    "    A_prev_pad = zero_pad(A_prev,pad)\n",
    "    dA_prev_pad = zero_pad(dA_prev,pad)\n",
    "    \n",
    "    #现在处理数据\n",
    "    for i in range(m):\n",
    "        #选择第i个扩充了的数据的样本,降了一维。\n",
    "        a_prev_pad = A_prev_pad[i]\n",
    "        da_prev_pad = dA_prev_pad[i]\n",
    "        \n",
    "        for h in range(n_H):\n",
    "            for w in range(n_W):\n",
    "                for c in range(n_C):\n",
    "                    #定位切片位置\n",
    "                    vert_start = h\n",
    "                    vert_end = vert_start + f\n",
    "                    horiz_start = w\n",
    "                    horiz_end = horiz_start + f\n",
    "                    \n",
    "                    #定位完毕，开始切片\n",
    "                    a_slice = a_prev_pad[vert_start:vert_end,horiz_start:horiz_end,:]\n",
    "                    \n",
    "                    #切片完毕，使用上面的公式计算梯度\n",
    "                    da_prev_pad[vert_start:vert_end, horiz_start:horiz_end,:] += W[:,:,:,c] * dZ[i, h, w, c]\n",
    "                    dW[:,:,:,c] += a_slice * dZ[i,h,w,c]\n",
    "                    db[:,:,:,c] += dZ[i,h,w,c]\n",
    "        #设置第i个样本最终的dA_prev,即把非填充的数据取出来。\n",
    "        dA_prev[i,:,:,:] = da_prev_pad[pad:-pad, pad:-pad, :]\n",
    "    \n",
    "    #数据处理完毕，验证数据格式是否正确\n",
    "    assert(dA_prev.shape == (m, n_H_prev, n_W_prev, n_C_prev))\n",
    "    \n",
    "    return (dA_prev,dW,db)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "dA_mean = 9.608990675868995\n",
      "dW_mean = 10.581741275547563\n",
      "db_mean = 76.37106919563735\n"
     ]
    }
   ],
   "source": [
    "np.random.seed(1)\n",
    "#初始化参数\n",
    "A_prev = np.random.randn(10,4,4,3)\n",
    "W = np.random.randn(2,2,3,8)\n",
    "b = np.random.randn(1,1,1,8)\n",
    "hparameters = {\"pad\" : 2, \"stride\": 1}\n",
    "\n",
    "#前向传播\n",
    "Z , cache_conv = conv_forward(A_prev,W,b,hparameters)\n",
    "#反向传播\n",
    "dA , dW , db = conv_backward(Z,cache_conv)\n",
    "print(\"dA_mean =\", np.mean(dA))\n",
    "print(\"dW_mean =\", np.mean(dW))\n",
    "print(\"db_mean =\", np.mean(db))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 最大值池化层的反向传播"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [],
   "source": [
    "def create_mask_from_window(x):\n",
    "    \"\"\"\n",
    "    从输入矩阵中创建掩码，以保存最大值的矩阵的位置。\n",
    "    \n",
    "    参数：\n",
    "        x - 一个维度为(f,f)的矩阵\n",
    "        \n",
    "    返回：\n",
    "        mask - 包含x的最大值的位置的矩阵\n",
    "    \"\"\"\n",
    "    mask = x == np.max(x)\n",
    "    \n",
    "    return mask"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x = [[ 1.62434536 -0.61175641 -0.52817175]\n",
      " [-1.07296862  0.86540763 -2.3015387 ]]\n",
      "mask = [[ True False False]\n",
      " [False False False]]\n"
     ]
    }
   ],
   "source": [
    "np.random.seed(1)\n",
    "\n",
    "x = np.random.randn(2,3)\n",
    "\n",
    "mask = create_mask_from_window(x)\n",
    "\n",
    "print(\"x = \" + str(x)) \n",
    "print(\"mask = \" + str(mask))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 均值池化层的反向传播"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [],
   "source": [
    "def distribute_value(dz,shape):\n",
    "    \"\"\"\n",
    "    给定一个值，为按矩阵大小平均分配到每一个矩阵位置中。\n",
    "    \n",
    "    参数：\n",
    "        dz - 输入的实数\n",
    "        shape - 元组，两个值，分别为n_H , n_W\n",
    "        \n",
    "    返回：\n",
    "        a - 已经分配好了值的矩阵，里面的值全部一样。\n",
    "    \n",
    "    \"\"\"\n",
    "    #获取矩阵的大小\n",
    "    (n_H , n_W) = shape\n",
    "    \n",
    "    #计算平均值\n",
    "    average = dz / (n_H * n_W)\n",
    "    \n",
    "    #填充入矩阵\n",
    "    a = np.ones(shape) * average\n",
    "    \n",
    "    return a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a = [[0.5 0.5]\n",
      " [0.5 0.5]]\n"
     ]
    }
   ],
   "source": [
    "dz = 2\n",
    "shape = (2,2)\n",
    "\n",
    "a = distribute_value(dz,shape)\n",
    "print(\"a = \" + str(a))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [],
   "source": [
    "def pool_backward(dA,cache,mode = \"max\"):\n",
    "    \"\"\"\n",
    "    实现池化层的反向传播\n",
    "    \n",
    "    参数:\n",
    "        dA - 池化层的输出的梯度，和池化层的输出的维度一样\n",
    "        cache - 池化层前向传播时所存储的参数。\n",
    "        mode - 模式选择，【\"max\" | \"average\"】\n",
    "        \n",
    "    返回：\n",
    "        dA_prev - 池化层的输入的梯度，和A_prev的维度相同\n",
    "    \n",
    "    \"\"\"\n",
    "    #获取cache中的值\n",
    "    (A_prev , hparaeters) = cache\n",
    "    \n",
    "    #获取hparaeters的值\n",
    "    f = hparaeters[\"f\"]\n",
    "    stride = hparaeters[\"stride\"]\n",
    "    \n",
    "    #获取A_prev和dA的基本信息\n",
    "    (m , n_H_prev , n_W_prev , n_C_prev) = A_prev.shape\n",
    "    (m , n_H , n_W , n_C) = dA.shape\n",
    "    \n",
    "    #初始化输出的结构\n",
    "    dA_prev = np.zeros_like(A_prev)\n",
    "    \n",
    "    #开始处理数据\n",
    "    for i in range(m):\n",
    "        a_prev = A_prev[i]      \n",
    "        for h in range(n_H):\n",
    "            for w in range(n_W):\n",
    "                for c in range(n_C):\n",
    "                    #定位切片位置\n",
    "                    vert_start = h\n",
    "                    vert_end = vert_start + f\n",
    "                    horiz_start = w\n",
    "                    horiz_end = horiz_start + f\n",
    "                    \n",
    "                    #选择反向传播的计算方式\n",
    "                    if mode == \"max\":\n",
    "                        #开始切片\n",
    "                        a_prev_slice = a_prev[vert_start:vert_end,horiz_start:horiz_end,c]\n",
    "                        #创建掩码\n",
    "                        mask = create_mask_from_window(a_prev_slice)\n",
    "                        #计算dA_prev\n",
    "                        dA_prev[i,vert_start:vert_end,horiz_start:horiz_end,c] += np.multiply(mask,dA[i,h,w,c])\n",
    "    \n",
    "                    elif mode == \"average\":\n",
    "                        #获取dA的值\n",
    "                        da = dA[i,h,w,c]\n",
    "                        #定义过滤器大小\n",
    "                        shape = (f,f)\n",
    "                        #平均分配\n",
    "                        dA_prev[i,vert_start:vert_end, horiz_start:horiz_end ,c] += distribute_value(da,shape)\n",
    "    #数据处理完毕，开始验证格式\n",
    "    assert(dA_prev.shape == A_prev.shape)\n",
    "    \n",
    "    return dA_prev"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "mode = max\n",
      "mean of dA =  0.14571390272918056\n",
      "dA_prev[1,1] =  [[ 0.          0.        ]\n",
      " [ 5.05844394 -1.68282702]\n",
      " [ 0.          0.        ]]\n",
      "\n",
      "mode = average\n",
      "mean of dA =  0.14571390272918056\n",
      "dA_prev[1,1] =  [[ 0.08485462  0.2787552 ]\n",
      " [ 1.26461098 -0.25749373]\n",
      " [ 1.17975636 -0.53624893]]\n"
     ]
    }
   ],
   "source": [
    "np.random.seed(1)\n",
    "A_prev = np.random.randn(5, 5, 3, 2)\n",
    "hparameters = {\"stride\" : 1, \"f\": 2}\n",
    "A, cache = pool_forward(A_prev, hparameters)\n",
    "dA = np.random.randn(5, 4, 2, 2)\n",
    "\n",
    "dA_prev = pool_backward(dA, cache, mode = \"max\")\n",
    "print(\"mode = max\")\n",
    "print('mean of dA = ', np.mean(dA))\n",
    "print('dA_prev[1,1] = ', dA_prev[1,1])  \n",
    "print()\n",
    "dA_prev = pool_backward(dA, cache, mode = \"average\")\n",
    "print(\"mode = average\")\n",
    "print('mean of dA = ', np.mean(dA))\n",
    "print('dA_prev[1,1] = ', dA_prev[1,1]) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 神经网络的应用\n",
    "- tf模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "metadata": {},
   "outputs": [],
   "source": [
    "import math\n",
    "import numpy as np\n",
    "import h5py\n",
    "import matplotlib.pyplot as plt\n",
    "import matplotlib.image as mpimg\n",
    "\n",
    "import tensorflow.compat.v1 as tf\n",
    "tf.disable_v2_behavior()\n",
    "\n",
    "# import tensorflow as tf\n",
    "\n",
    "from tensorflow.python.framework import ops\n",
    "\n",
    "from do_not_src import cnn_utils\n",
    "\n",
    "%matplotlib inline\n",
    "np.random.seed(1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "y = 2\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "X_train_orig , Y_train_orig , X_test_orig , Y_test_orig , classes = cnn_utils.load_dataset()\n",
    "\n",
    "\n",
    "index = 6\n",
    "plt.imshow(X_train_orig[index])\n",
    "print (\"y = \" + str(np.squeeze(Y_train_orig[:, index])))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "number of training examples = 1080\n",
      "number of test examples = 120\n",
      "X_train shape: (1080, 64, 64, 3)\n",
      "Y_train shape: (1080, 6)\n",
      "X_test shape: (120, 64, 64, 3)\n",
      "Y_test shape: (120, 6)\n"
     ]
    }
   ],
   "source": [
    "X_train = X_train_orig/255.\n",
    "X_test = X_test_orig/255.\n",
    "Y_train = cnn_utils.convert_to_one_hot(Y_train_orig, 6).T\n",
    "Y_test = cnn_utils.convert_to_one_hot(Y_test_orig, 6).T\n",
    "print (\"number of training examples = \" + str(X_train.shape[0]))\n",
    "print (\"number of test examples = \" + str(X_test.shape[0]))\n",
    "print (\"X_train shape: \" + str(X_train.shape))\n",
    "print (\"Y_train shape: \" + str(Y_train.shape))\n",
    "print (\"X_test shape: \" + str(X_test.shape))\n",
    "print (\"Y_test shape: \" + str(Y_test.shape))\n",
    "conv_layers = {}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "metadata": {},
   "outputs": [],
   "source": [
    "def create_placeholders(n_H0, n_W0, n_C0, n_y):\n",
    "    \"\"\"\n",
    "    为session创建占位符\n",
    "    \n",
    "    参数：\n",
    "        n_H0 - 实数，输入图像的高度\n",
    "        n_W0 - 实数，输入图像的宽度\n",
    "        n_C0 - 实数，输入的通道数\n",
    "        n_y  - 实数，分类数\n",
    "        \n",
    "    输出：\n",
    "        X - 输入数据的占位符，维度为[None, n_H0, n_W0, n_C0]，类型为\"float\"\n",
    "        Y - 输入数据的标签的占位符，维度为[None, n_y]，维度为\"float\"\n",
    "    \"\"\"\n",
    "    X = tf.placeholder(tf.float32,[None, n_H0, n_W0, n_C0])\n",
    "    Y = tf.placeholder(tf.float32,[None, n_y])\n",
    "    \n",
    "    return X,Y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "X = Tensor(\"Placeholder:0\", shape=(?, 64, 64, 3), dtype=float32)\n",
      "Y = Tensor(\"Placeholder_1:0\", shape=(?, 6), dtype=float32)\n"
     ]
    }
   ],
   "source": [
    "X , Y = create_placeholders(64,64,3,6)\n",
    "print (\"X = \" + str(X))\n",
    "print (\"Y = \" + str(Y))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "metadata": {},
   "outputs": [],
   "source": [
    "def initialize_parameters():\n",
    "    \"\"\"\n",
    "    初始化权值矩阵，这里我们把权值矩阵硬编码：\n",
    "    W1 : [4, 4, 3, 8]\n",
    "    W2 : [2, 2, 8, 16]\n",
    "    \n",
    "    返回：\n",
    "        包含了tensor类型的W1、W2的字典\n",
    "    \"\"\"\n",
    "    tf.set_random_seed(1)\n",
    "    \n",
    "    W1 = tf.get_variable(\"W1\",[4,4,3,8],initializer=tf.contrib.layers.xavier_initializer(seed=0))\n",
    "    W2 = tf.get_variable(\"W2\",[2,2,8,16],initializer=tf.contrib.layers.xavier_initializer(seed=0))\n",
    "    \n",
    "    parameters = {\"W1\": W1,\n",
    "                  \"W2\": W2}\n",
    "    \n",
    "    return parameters"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "ename": "AttributeError",
     "evalue": "module 'tensorflow_core.compat.v1' has no attribute 'contrib'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-70-2a44f1b0d649>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0mtf\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mreset_default_graph\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      2\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mtf\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mSession\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0msess_test\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m     \u001b[0mparameters\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0minitialize_parameters\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      4\u001b[0m     \u001b[0minit\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtf\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mglobal_variables_initializer\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      5\u001b[0m     \u001b[0msess_test\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrun\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minit\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m<ipython-input-69-3bce7b94d804>\u001b[0m in \u001b[0;36minitialize_parameters\u001b[0;34m()\u001b[0m\n\u001b[1;32m     10\u001b[0m     \u001b[0mtf\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset_random_seed\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     11\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 12\u001b[0;31m     \u001b[0mW1\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtf\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_variable\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"W1\"\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m3\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m8\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0minitializer\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mtf\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcontrib\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlayers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mxavier_initializer\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mseed\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     13\u001b[0m     \u001b[0mW2\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtf\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_variable\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"W2\"\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m8\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m16\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0minitializer\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mtf\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcontrib\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlayers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mxavier_initializer\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mseed\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     14\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mAttributeError\u001b[0m: module 'tensorflow_core.compat.v1' has no attribute 'contrib'"
     ]
    }
   ],
   "source": [
    "tf.reset_default_graph()\n",
    "with tf.Session() as sess_test:\n",
    "    parameters = initialize_parameters()\n",
    "    init = tf.global_variables_initializer()\n",
    "    sess_test.run(init)\n",
    "    print(\"W1 = \" + str(parameters[\"W1\"].eval()[1,1,1]))\n",
    "    print(\"W2 = \" + str(parameters[\"W2\"].eval()[1,1,1]))\n",
    "    \n",
    "    sess_test.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "~~遇到问题，'tensorflow_core.compat.v1' has no attribute 'contrib'~~"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
